/**
 * Copyright (C) 2025 pony working team
 * SPDX-License-Identifier: MulanPSL-2.0
 */

/* includes (local) ----------------------------------------------------------*/
#include "hal_it.h"
/* includes (standard library, system) ---------------------------------------*/
#include <stddef.h>
/* includes (other library) --------------------------------------------------*/
/* includes (project) --------------------------------------------------------*/
#include <x_config.h>
#include <x_bug.h>
#include <x_warn.h>
#include <x_errno.h>

/* defines -------------------------------------------------------------------*/
/* typedefs ------------------------------------------------------------------*/
/* macro ---------------------------------------------------------------------*/
/* variables (extern) --------------------------------------------------------*/
/* variables (global) --------------------------------------------------------*/

unsigned int it_cnt = 0;

__section__(stack) __unused char irq_stack[CONFIG_IRQ_STACK_SIZE];

/* variables (local) ---------------------------------------------------------*/

static struct {
    hal_it_handler_t handler;
    uintptr_t arg;
} __irq_table[CONFIG_NR_IRQS];

static const hal_it_methods_t *__intc_methods = NULL;

/* inlines -------------------------------------------------------------------*/
/* functions -----------------------------------------------------------------*/

/**
 * hal_it_dispatch - run interrupt routine for an irq
 * @irq: the irq number
 *
 * return: NA
 */
void hal_it_dispatch(unsigned int irq)
{
    WARN_ON(irq >= CONFIG_NR_IRQS, errno = ERRNO_HAL_INTC_ILLEGAL_IRQN; return, "IRQn too large!");

    WARN_ON(__irq_table[irq].handler == NULL, return, "IRQ (%d) not connected!", irq);

    __irq_table[irq].handler(__irq_table[irq].arg);
}

/**
 * hal_it_connect - connect a routine to a hardware interrupt
 * @irq:     the irq number to attach to
 * @handler: irq handler to be installed
 * @arg:     argument for the handler
 *
 * return: 0 on success, negative value on error
 */
int hal_it_connect(unsigned int irq, hal_it_handler_t handler, uintptr_t arg)
{
    WARN_ON(irq >= CONFIG_NR_IRQS, errno = ERRNO_HAL_INTC_ILLEGAL_IRQN;
            return -1, "IRQn too large!");

    WARN_ON(__irq_table[irq].handler != NULL, errno = ERRNO_HAL_INTC_ILLEGAL_OPERATION;
            return -1, "IRQ (%d) already connected!", irq);

    __irq_table[irq].arg = arg;
    __irq_table[irq].handler = handler;

    return 0;
}

/**
 * hal_it_disconnect - disconnect a routine to a hardware interrupt
 * @irq: the irq number to be attached
 *
 * return: 0 on success, negative value on error
 */
int hal_it_disconnect(unsigned int irq)
{
    WARN_ON(irq >= CONFIG_NR_IRQS, errno = ERRNO_HAL_INTC_ILLEGAL_IRQN;
            return -1, "IRQn too large!");

    __irq_table[irq].handler = NULL;

    return 0;
}

/**
 * hal_it_setprio - set the priority of a specific irq
 * @irq:  the irq number
 * @prio: the priority of the irq
 *
 * return: 0 on success, negative value on error
 */
int hal_it_setprio(unsigned int irq, unsigned int prio)
{
    BUG_ON(__intc_methods == NULL, "No interrupt controller installed!");

    WARN_ON(__intc_methods->setprio == NULL, errno = ERRNO_HAL_INTC_ILLEGAL_OPERATION;
            return -1, "Interrupt controller has no <setprio> method!");

    return __intc_methods->setprio(irq, prio);
}

/**
 * hal_it_enable - enable a specific irq
 * @irq:  the irq number to be enabled
 *
 * return: 0 on success, negative value on error
 */
int hal_it_enable(unsigned int irq)
{
    BUG_ON(__intc_methods == NULL, "No interrupt controller installed!");

    // TODO: devfs register

    return __intc_methods->enable(irq);
}

/**
 * hal_it_disable - disable a specific irq
 * @irq: the irq number to be disabled
 *
 * return: 0 on success, negative value on error
 */
int hal_it_disable(unsigned int irq)
{
    BUG_ON(__intc_methods == NULL, "No interrupt controller installed!");

    WARN_ON(__intc_methods->disable == NULL, errno = ERRNO_HAL_INTC_ILLEGAL_OPERATION;
            return -1, "Interrupt controller has no <setprio> disable!");

    return __intc_methods->disable(irq);
}

/**
 * hal_it_handler - interrupt handler
 * @irq: the irq number to be disabled
 *
 * return: 0 on success, negative value on error
 */
void hal_it_handler(void)
{
    BUG_ON(__intc_methods == NULL, "No interrupt controller installed!");

    if (unlikely(__intc_methods->handler == NULL)) {
        return;
    }

    __intc_methods->handler();
}

/**
 * hal_it_register - register an interrupt controler
 * @methods:   the interrupt controler methods
 *
 * return: 0 on success, negative value on error
 */
int hal_it_register(const hal_it_methods_t *methods)
{
    BUG_ON(methods == NULL || methods->enable == NULL, "Invalid interrupt methods!");

    __intc_methods = methods;

    // TODO: devfs register

    return 0;
}
